/*
call 、 apply 和 bind 是JavaScript中的三种函数调用模式，都可以用来改变函数的上下文(即函数内部的 this 指向)。
call 方法和 apply 方法作用相同，都是用来改变函数的上下文。它们的区别在于传递参数的不同。
call 方法需要将每个参数单独传递给函数，而 apply 方法则需要将参数作为数组传递给函数。
bind 方法也可以改变函数的上下文，但与 call 和 apply 不同的是，它不会立即调用函数，而是返回一个新的函数，新函数的上下文被绑定到 bind 方法的第一个参数

globalThis （全局对象）是一个新的内置对象，它是在ECMAScript 2020中引入的。它提供了一种跨平台、跨环境访问全局对象的方法，无论是在Web浏览器中还是在Node.js中运行的JavaScript代码，都可以使用 globalThis 来访问全局对象。
在Web浏览器中，全局对象是 window 对象，而在Node.js中，全局对象是 global 对象。 globalThis 允许我们在这两个环境中使用相同的语法来访问全局对象。
 */
Function.prototype.MyCall = function (obj, ...args) {
    // 全局属性 globalThis 包含全局的 this 值，类似于全局对象
    /*
        首先，判断 obj 是否为 null 或者 undefined ，如果是的话，执行右侧的表达式。如果 obj 不是 null 或者 undefined ，则不执行右侧的表达式。
        右侧的表达式将全局对象( globalThis )赋值给 obj
        这段代码可以用于确保某个对象在特定作用域中可用。
        如果在某个作用域中需要使用某个对象，但是无法确定它是否在该作用域中定义，可以使用这个模式来确保对象被绑定到该作用域中，使其可用。
     */
    !obj && (obj = globalThis);
    // this代表要执行的函数 这里即给出了函数给obj的上下文
    obj._fn = this;
    // obj来执行fn
    const res = obj._fn(...args);
    // 执行完就删了
    delete obj._fn;
    // return结果
    return res;
};

Function.prototype.MyApply = function (obj, args) {
    // 第二个参数必须为数组或类数组对象, 否则初始化为空对象
    const arr = [];
    // 处理数组呗
    for (let i = 0; i < args?.length; ++i) {
        arr.push(args[i]);
    }
    // 展开数组 用call
    return this.MyCall(obj, ...arr);
};
// 它不会立即调用函数，而是返回一个新的函数，新函数的上下文被绑定到 bind 方法的第一个参数
Function.prototype.MyBind = function (obj, ...args1) {
    !obj && (obj = globalThis);
    return (...args2) => {
        obj._fn = this;
        const res = obj._fn(...[...args1, ...args2]);
        delete obj._fn;
        return res;
    };
};
